
rem
bbdoc: Quad Tree entity system.
about: Required components: TPosition, THitBox, TTransform, TQuadTree

	Entities can be in multiple nodes at the same time.	

end rem
Type TQuadTreeSystem Extends TEntityProcessingSystem
	
	Field _positionMapper:TComponentMapper
	Field _hitboxMapper:TComponentMapper
	Field _transformMapper:TComponentMapper
	Field _quadTreeMapper:TComponentMapper
	
	'maximum amount of entities in one node.
	Field _maxNodeEntities:Int = 50
	
	'world area
	Field _area:TRect
	
	'start of tree. head node.
	Field _head:TQuadTreeNode
	
	
	Method Initialize()
		_positionMapper = TComponentMapper.Create(New TPositionComponent, _world)
		_hitboxMapper = TComponentMapper.Create(New THitBoxComponent, _world)
		_transformMapper = TComponentMapper.Create(New TTransformComponent, _world)
		_quadTreeMapper = TComponentMapper.Create(New TQuadTreeComponent, _world)

		'set global component mappers: easy access for nodes
		TQuadTreeNode._hitboxMapper = _hitboxMapper
		TQuadTreeNode._positionMapper = _positionMapper
		TQuadTreeNode._quadTreeMapper = _quadTreeMapper
		TQuadTreeNode._transformMapper = _transformMapper

		RegisterComponent(New TPositionComponent)
		RegisterComponent(New THitBoxComponent)
		RegisterComponent(New TQuadTreeComponent)
		RegisterComponent(New TTransformComponent)
		
		'tree root
		_head = TQuadTreeNode.Create(-25, 25, 50, 50, Self, _maxNodeEntities)
		
		'create 50x50 world, centered on 0,0, so -25,25 to 25,25
		_area = TRect.Create(0, 0, 50, 50, True)
	End Method
	

	Method ProcessEntity(e:TEntity)
		Local p:TPositionComponent = TPositionComponent(_positionMapper.Get(e))
		Local i:TQuadTreeComponent = TQuadTreeComponent(_quadTreeMapper.Get(e))

		'move entity in quadtree when position has changed
		If p.Changed() Then i.GetParentNode().MoveEntity(e)
	
'		If p.Changed()
'			For Local node:TQuadTreeNode = EachIn i.GetParentnodes()
'				node.MoveEntity(e)
'			Next
'		End If
	
	End Method
	

	'called when an entity has been added to the quad tree system.
	Method Added(e:TEntity)
		Local r:TRect = Self.GetEntityRect(e)
		'see if entity fits inside the world		
		If Not _area.Inside(r) Then Self.Resize(r)
		
		'start insertion
		_head.InsertEntity(e)
	End Method
	
	
	'called when an entity is removed from the quadtree system.
	Method Removed(e:TEntity)
		_head.RemoveEntity(e)
	End Method	
	
	
	Rem
	bbdoc: Set QuadTree world area.
	about: Not centered on 0,0!
	endrem
	Method SetArea(x:Int, y:Int, w:Int, h:Int)
		Resize(TRect.Create(x, y, w, h))
	End Method

	
	Rem
	bbdoc: Sets the maximum amount of entities before a node is subdivided.
	about: Default is 50.
	endrem
	Method SetMaxNodeEntities(m:Int)
		_maxNodeEntities = m
	End Method
	
	
	'Resizes the world so passed rect fits inside.
	Method Resize(r:TRect)

		'increase world rect until specified rect fits.
		While Not _area.Inside(r)
			_area.GetPosition().Multiply(2)
			_area.GetDimension().Multiply(2)
		Wend
		
		'done		
		'save all entities in the tree
		Local l:TList = New TList
		_head.GetAllEntities(l)
		
		'cut down tree
		_head.Free()
		_head = Null
		
		'create a new root using new area size.
		_head = New TQuadTreeNode.Create(_area.GetX(), _area.GetY(),  ..
			_area.GetWidth(), _area.GetHeight(), Self, _maxNodeEntities, Null)
		
		're-insert all entities
		For Local e:TEntity = EachIn l
			_head.InsertEntity(e)
		Next
	End Method
	
			
	Rem
	bbdoc: Returns all entities overlapping specified rect.
	returns: TBag
	endrem
	Method GetEntities:TBag(r:TRect)
		Local b:TBag = New TBag
		_head.GetEntities(b, r)
		Return b
	End Method

	
	Rem
	bbdoc: Returns a list of ALL entities in this tree.
	endrem	
	Method GetAllEntities:TList()
		Local l:TList = New TList
		_head.GetAllEntities(l)
		Return l
	End Method
	
	
	'returns entity position point.
	Method GetEntityPoint:TVector2D(e:TEntity)
		Local p:TPositionComponent = TPositionComponent(_positionMapper.Get(e))
		Return p.Get()
	End Method
	
	
	'returns entity rect, centered on position, scaled to entity.
	Method GetEntityRect:TRect(e:TEntity)
		Local p:TPositionComponent = TPositionComponent(_positionMapper.Get(e))
		Local c:THitBoxComponent = THitBoxComponent(_hitboxMapper.Get(e))
		Local t:TTransformComponent = TTransformComponent(_transformMapper.Get(e))
		Local scale:Float = Max(t.GetScaleX(), t.GetScaleY())
		Return TRect.Create(p.GetX(), p.GetY(), c.GetWidth() * scale, c.GetHeight() * scale, True)
	End Method	
	

	'for debugging purposes
	Method Render()
		SetBlend(ALPHABLEND)
		SetRotation(0)
		SetAlpha 0.1
		SetColor 255, 255, 255
		_head.Render()
	End Method

End Type
